#!/bin/bash -e
#   * compile instructions: execute:
#   *
#   * ```
#   *     chmod +x code-scanner.cs
#   *     ./code-scanner.cs ${GAME_BASE_DIR}
#   * ```
#   *
#   * then the scanner will be compiled, which may generate huge
#   * amount of logs according to your settings.
#   *
#   * Here we wrote a shebang like file, which works just fine
#   * in my computer (Manjaro XFCE), if such script do not work
#   * in your computer, you could just try the instructions below :
export IFS=$'\n' # to disable the annoying space.
export DOTNET=dotnet # the location of the DOTNET executable file.
[ -z "$DOTNET_CSC_DLL" ] && export DOTNET_CSC_DLL=`\ls /usr/share/dotnet/sdk/*/Roslyn/bincore/csc.dll` # In manjaro, the csc.dll is located in /usr/share/dotnet/sdk/*/Roslyn/bincore/csc.dll

export GAME_BASE_DIR="$1" # this should be modified.

__MODE_VERBOSE=56 # is the line number of "#define VERBOSE", may be modified
__MODE_DEBUG__=$((__MODE_VERBOSE+1))
__MODE_RELEASE=$((__MODE_DEBUG__+1))
_MODE__SELECT_=$__MODE_DEBUG__

export FILE_NAME="$0"
export PLUGIN_ID="Neutron3529.Scanner"
export NAMESPACE_ID="Neutron3529.Scanner"

( yes "" | head -n $_MODE__SELECT_ | head -n-1  ; tail $FILE_NAME -n+$_MODE__SELECT_ ) | sed s/%%NAMESPACE_ID%%/${NAMESPACE_ID}/g | sed s/%%PLUGIN_ID%%/${PLUGIN_ID}/g | $DOTNET $DOTNET_CSC_DLL -nologo -t:library \
  $(for i in "${GAME_BASE_DIR}/BepInEx/core/"*.dll ; do echo -e "-r:\"$i\"\n" ; done) \
  $(for i in "${GAME_BASE_DIR}/BepInEx/unhollowed/"*.dll ; do echo -e "-r:\"$i\"\n" ; done) \
  $(for i in "${GAME_BASE_DIR}/mono/Managed/"*.dll ; do echo -e "-r:\"$i\"\n" ; done) \
  -out:"${GAME_BASE_DIR}/BepInEx/plugins/${FILE_NAME%.*}".dll \
  -optimize `[ x"$_MODE__SELECT_" == x"$__MODE_DEBUG__" ] && echo -debug` \
  - && rm -f "${GAME_BASE_DIR}/BepInEx/config/${PLUGIN_ID}.cfg";

if [ -n "$2" ]; then
    git add ${FILE_NAME}
    case $2 in
        R) git commit -am "`curl -s http://whatthecommit.com/index.txt`" ;;
        r) git commit -am "`curl -s http://whatthecommit.com/index.txt`" ;;
        RANDOM) git commit -am "`curl -s http://whatthecommit.com/index.txt`" ;;
        random) git commit -am "`curl -s http://whatthecommit.com/index.txt`" ;;
        U) git commit -am "`curl -s http://whatthecommit.com/index.txt`" ;;
        u) git commit -am "`curl -s http://whatthecommit.com/index.txt`" ;;
        UPLOAD) git commit -am "`curl -s http://whatthecommit.com/index.txt`" ;;
        upload) git commit -am "`curl -s http://whatthecommit.com/index.txt`" ;;
        *) git commit -am "$2" ;;
    esac
    git push
fi
exit

#define VERBOSE // the line of __MODE_VERBOSE
#define DEBUG




using System;
using System.Linq;
using System.Reflection;
using System.Reflection.Emit;
using System.Collections.Generic;

using BepInEx;
using BepInEx.IL2CPP;
using BepInEx.Configuration;
using HarmonyLib;
using UnityEngine;

namespace %%NAMESPACE_ID%%
{
    [BepInPlugin("%%PLUGIN_ID%%", "%%NAMESPACE_ID%%", "0.1.0")]
    class Cheat : BasePlugin {
        static Action<string> logger;
        static int count=0;
        static Type type_of_class_in_the_scanning_assembly=typeof(void); // change to anything except `typeof(void)` would disable the list of patch_types.
        static Type[] patch_types=new Type[]{ // scanning only the following type, will not affect other method in the same assembly.
            typeof(VCBattler),
            typeof(AdventureData),
//             typeof(NussygameCore),
        };
        public override void Load() {
            var harmony=new Harmony("%%PLUGIN_ID%%");
            logger=Log.LogInfo;

//             if(Config.Bind("config", "scan methods", true, "enable logging most of the methods.").Value){
//                 foreach(Type ty in patch_types[0].Assembly.GetTypes()){
//                     MethodInfo[] m=ty.GetMethods(BindingFlags.DeclaredOnly | BindingFlags.Public | BindingFlags.Static);
//                     foreach(MethodInfo i in m){
//                         bool non_generic=!i.IsGenericMethod;
//                         bool not_getter_setter=!(i.Name.Length>4 && ((i.Name[0]=='g' || i.Name[0]=='s') && i.Name[1]=='e' && i.Name[2]=='t' && i.Name[3]=='_'));
//                         logger("scanning: "+i.DeclaringType+" - "+i+", non_generic="+non_generic+", not_getter_setter="+not_getter_setter);
//                     }
//                 }
//             }

            harmony.PatchAll(typeof(Scanner));
            logger("scanner started.");
        }
        class Scanner {
            static IEnumerable<MethodBase> TargetMethods() {
                HashSet<MethodInfo> hashset = new HashSet<MethodInfo>();
                List<MethodInfo> list =  new List<MethodInfo>();
                if(type_of_class_in_the_scanning_assembly!=typeof(void)){
                    patch_types=type_of_class_in_the_scanning_assembly.Assembly.GetTypes(); // would generate too much patcher, make harmony failed to patch.
                }
                foreach(Type ty in patch_types){
                    MethodInfo[] m=ty.GetMethods((BindingFlags)(-1));
                    foreach(MethodInfo i in m){
                        if(i.DeclaringType==ty)if(hashset.Add(i))list.Add(i);
                    }
                }
                foreach(MethodInfo i in list){
                    bool non_generic=!i.IsGenericMethod;
                    bool not_getter_setter=!(i.Name.Length>4 && ((i.Name[0]=='g' || i.Name[0]=='s') && i.Name[1]=='e' && i.Name[2]=='t' && i.Name[3]=='_'));
                    logger("targeting: "+i.DeclaringType+" - "+i+", non_generic="+non_generic+", not_getter_setter="+not_getter_setter);
                    if(non_generic && not_getter_setter)yield return i;
                }
            }
            static bool Prepare(MethodBase __originalMethod){
                if(__originalMethod==null){
                    logger("first prepare, should return true.");
                    return true;
                }
                bool non_generic=!__originalMethod.IsGenericMethod;
                bool not_getter_setter=!(__originalMethod.Name.Length>4 && ((__originalMethod.Name[0]=='g' || __originalMethod.Name[0]=='s') && __originalMethod.Name[1]=='e' && __originalMethod.Name[2]=='t' && __originalMethod.Name[3]=='_'));
                logger("prepare "+__originalMethod+", non_generic="+non_generic+", not_getter_setter="+not_getter_setter);
                return non_generic && not_getter_setter;
            }
            static void Prefix(MethodBase __originalMethod) {
                count++;
                logger("Call "+count+" "+__originalMethod.DeclaringType+" - "+__originalMethod);
//                 logger("Call "+list.Count+" "+__originalMethod);
//                 list.Push(__originalMethod.ToString());
            }
            static void Postfix(MethodBase __originalMethod) {
                logger("Done "+count+" "+__originalMethod.DeclaringType+" - "+__originalMethod);
                count--;
//                 if(__originalMethod.ToString()==list.Pop()){
//                     logger("Done "+list.Count+" "+__originalMethod);
//                 } else {
//                     logger("push/pop mismatch");
//                 }
            }
        }
    }
}
